package cabinet

import (
	"fmt"
	"github.com/pkg/errors"
	"math"
	"time"
	"zym.com/tools/crc"
	"zym.com/tools/zconst"
	"zym.com/tools/zconvert/conv"
	"zym.com/tools/zfile"
	"zym.com/tools/zserial"
)

var serial zserial.ComPort
var n uint8 = 0 // 流水号
var chanCmd = make(chan byte, 1)

func Run(p string) (e error) {
	e = serial.Open(p, 115200, 1000, 0, false)
	if e == nil {
		serial.SetReadTimeout(1000)
	}

	return
}

func Stop() {
	serial.Close()
}

// OpenDoor 开门
func OpenDoor(channel uint8) (t bool, ori []byte, e error) {
	fmt.Println(time.Now().Format(zconst.TIME_yyyyMMddHHmmss), "-开门")
	chanCmd <- 1
	defer func() {
		<-chanCmd
		fmt.Println(time.Now().Format(zconst.TIME_yyyyMMddHHmmss), "->开门结束")
	}()
	o := OpenDoorInfo{}
	o.Number = getNumber()
	o.ChannelNo = channel
	var buf []byte
	var size int
	buf, e = packagingCommand(CmdOpenDoor, o)

	if _, e = writeData(buf); e != nil {
		fmt.Println(e)
		return
	}

	size, buf, e = readData(14)
	if e != nil {
		return
	}

	if buf[size-5] == 0 {
		t = true
	}
	ori = buf
	return
}

// SetCabinetSum 设置仓柜数量
func SetCabinetSum(sum uint8) (t bool, e error) {
	chanCmd <- 1
	defer func() {
		<-chanCmd
	}()
	o := CabSum{
		Sum: sum,
	}
	var buf []byte
	var size int
	buf, e = packagingCommand(CmdSetCabinetSum, o)

	if _, e = writeData(buf); e != nil {
		return
	}
	if size, buf, e = readData(13); e != nil {
		return
	}
	if buf[size-3] == 0 {
		t = true
	}
	return
}

// ChargeStart 开始充电
func ChargeStart(channel uint8) (t bool, ori []byte, e error) {
	chanCmd <- 1
	defer func() {
		<-chanCmd
	}()
	o := OpenDoorInfo{}
	o.Number = getNumber()
	o.ChannelNo = channel
	var buf []byte
	var size int
	buf, e = packagingCommand(CmdStartCharge, o)

	if _, e = writeData(buf); e != nil {
		return
	}

	if size, buf, e = readData(14); e != nil {
		return
	}

	if buf[size-5] == 0 {
		t = true
	}
	ori = buf
	return
}

func ReadCardNo() (cardNo uint32, ori []byte, e error) {
	chanCmd <- 1
	defer func() {
		<-chanCmd
	}()
	var buf []byte
	var size int
	buf, e = packagingCommand(CmdReadCardNo, nil)

	if _, e = writeData(buf); e != nil {
		return
	}

	if size, buf, e = readData(16); e != nil {
		return
	}

	if buf[size-7] == 0x01 {
		//cardNo = fmt.Sprintf("%x", buf[size-6:size-2])
		//_ = conv.BytesToObject(buf[size-6:size-2], conv.ASC, &cardNo)
		_ = conv.BytesToObject(buf[size-6:size-2], conv.DESC, &cardNo)
	}
	ori = buf
	return
}

func GetSoftwareVer() (ver string, e error) {
	chanCmd <- 1
	defer func() {
		time.Sleep(100 * time.Millisecond)
		<-chanCmd
	}()
	buff := []byte{0x3a, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x09}
	c := crc.CheckCrc16(buff, len(buff))
	buff = append(buff, byte(c>>8), byte(c))

	var n int
	if n, e = writeData(buff); e != nil {
		return
	}
	if n < len(buff) {
		e = errors.New("write data size error")
		return
	}
	readSize := 12 + 21
	if n, buff, e = readData(readSize); n < readSize {
		fmt.Println("receive error:", e.Error())
		fmt.Printf("%x", buff)
		return
	}
	//_ = conv.BytesToObject(buff[16:20], conv.ASC, &ver)
	fmt.Printf("%x\n", buff)
	fmt.Printf("设备类型：%x\n软件版本：%x\n版本号串：%s", buff[12:16], buff[16:20], string(buff[20:n-2]))
	ver = string(buff[20 : n-2])
	return
}

// Update 主板程序升级
func Update(fileName string) (e error) {
	chanCmd <- 1
	defer func() {
		time.Sleep(50 * time.Millisecond)
		<-chanCmd
	}()
	if e = handshake(); e != nil {
		return errors.New("handshake:" + e.Error())
	}
	head := []byte{0x3a, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x02}

	var bFile []byte
	if bFile, e = zfile.ReadFile(fileName); e != nil {
		return
	}
	bFileSize := uint32(len(bFile)) // 升级文件大小
	var frameLen uint16 = 512       // 帧大小小
	var checkSum uint32             // 升级文件和校验
	var offset uint32               // 偏移量
	var frameSum uint32             // 本帧和校验

	for i := 0; i < len(bFile); i++ {
		checkSum += uint32(bFile[i])
	}
	sendNum := bFileSize / uint32(frameLen)
	if bFileSize%uint32(frameLen) > 0 {
		sendNum++
	}
	retry := 0
	for i := 0; i < int(sendNum); {
		time.Sleep(50 * time.Millisecond)
		x := 0
		frameSum = 0
		var buff []byte
		var tmp []byte
		buff = append(buff, head...)
		// 升级文件总长度
		tmp, e = conv.ObjectToBytes(bFileSize, conv.ASC)
		buff = append(buff, tmp...)
		// 升级文件和校验
		tmp, e = conv.ObjectToBytes(checkSum, conv.ASC)
		buff = append(buff, tmp...)

		if offset+uint32(frameLen) > bFileSize {
			frameLen = uint16(bFileSize - offset)
		}

		var data []byte
		for j := offset; j < offset+uint32(frameLen); j++ {
			frameSum += uint32(bFile[j])
			data = append(data, bFile[j])
			x++
		}

		// 偏移量
		tmp, e = conv.ObjectToBytes(offset, conv.ASC)
		buff = append(buff, tmp...)

		// 本帧长度
		tmp, e = conv.ObjectToBytes(frameLen, conv.ASC)
		buff = append(buff, tmp...)
		// 本帧和校验
		tmp, e = conv.ObjectToBytes(frameSum, conv.ASC)
		buff = append(buff, tmp...)

		buff = append(buff, data...)
		//ob, size := getObjSize(o)
		dataLen := 20 + frameLen

		buff[8] = byte(dataLen >> 8 & 0x00ff)
		buff[9] = byte(dataLen)
		//buff = append(buff, ob...)
		c := crc.CheckCrc16(buff, len(buff))
		buff = append(buff, byte(c>>8), byte(c))

		var n int
		if n, e = writeData(buff); e != nil {
			return
		}
		if n < len(buff) {
			return errors.New("write data size error")
		}
		readSize := 12 + 21
		if n, buff, e = readData(readSize); e != nil {
			fmt.Println("receive update return error:", e.Error())
			fmt.Printf("%x", buff)
			return
		}
		fmt.Printf("receive update return:%x\n", buff)
		if n != readSize {
			return errors.New("read data size error")
		}
		if buff[readSize-3] == 0 {
			i++
			offset += uint32(frameLen)
			retry = 0
			fmt.Printf("第%d包成功\n", i)
		} else {
			if retry >= 5 {
				return errors.New("retry too frequency")
			}
			fmt.Printf("第%d包需重发\n", i)
			retry++
		}
	}
	return

}

// 升级第一步握手
func handshake() (e error) {
	var size int
	buff := []byte{0x3a, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x0f, 0x01, 0x65, 0x5a, 0xa1, 0x25, 0x5a, 0xa1}
	c := crc.CheckCrc16(buff, len(buff))
	buff = append(buff, byte(c>>8), byte(c))
	// 发送握手
	if size, e = writeData(buff); e != nil {
		return
	}
	if size == len(buff)+2 {
		return errors.New("write size error")
	}
	if size, buff, e = readData(22); e != nil { //22为握手回复长度
		return
	}
	if size != 22 {
		return errors.New("read size error")

	}
	return
}

// ChargeStop 停止充电
func ChargeStop(channel uint8) (t bool, ori []byte, e error) {
	chanCmd <- 1
	defer func() {
		<-chanCmd
	}()
	o := OpenDoorInfo{}
	o.Number = getNumber()
	o.ChannelNo = channel
	var buf []byte
	var size int
	buf, e = packagingCommand(CmdStopCharge, o)

	if _, e = writeData(buf); e != nil {
		return
	}
	fmt.Printf("%x\n", buf)
	if size, buf, e = readData(14); e != nil {
		return
	}

	if buf[size-5] == 0 {
		t = true
	}
	ori = buf
	return
}

// ReadCabStatus 读取设备状态
func ReadCabStatus() (cab StatusInfo, e error) {
	var buf []byte
	var size = 4096
	//fmt.Println(time.Now().Format(zconst.TIME_yyyyMMddHHmmss), "->读取设备数据")
	chanCmd <- 1
	defer func() {
		//fmt.Println(time.Now().Format(zconst.TIME_yyyyMMddHHmmss), "读取设备数据完成")
		<-chanCmd

	}()

	buf, e = packagingCommand(CmdReadStatus, nil)
	if _, e = writeData(buf); e != nil {
		return
	}
	_, size = getObjSize(cab)
	if size, buf, e = readData(size); e != nil {
		return
	}
	if len(buf) < size {
		if (size-40)%107 != 0 {
			e = errors.New("fail")
			return
		}
	}

	e = conv.BytesToObject(buf, conv.ASC, &cab)

	return
}

// 获取流水号
func getNumber() uint8 {
	if n == math.MaxUint8 {
		n = 1
	} else {
		n += 1
	}
	return n
}

// 写数据
func writeData(buf []byte) (s int, e error) {
	//fmt.Printf("发送：%x\n", buf)
	defer func() {
		if p := recover(); p != nil {
			//debug.PrintStack()
		}
	}()
	if s, e = serial.Write(buf); e != nil {
		return
	}
	return
}

// 读数据
func readData(s int) (size int, buf []byte, e error) {
	buf = make([]byte, s)
	size, e = serial.ReadFixedSize(buf, s)
	if size < s {
		if e != nil {
			return
		}
		if size == 0 {
			e = errors.New("not receive data")
			return
		}
		e = errors.New("data receive not full")
		//if e != nil && e.Error() == "EOF" {
		//	e = nil
		//}
		return
	}
	checkCrc := crc.CheckCrc16(buf, size-2)
	if byte(checkCrc) != buf[size-1] || byte(checkCrc>>8) != buf[size-2] {
		fmt.Println("crc error clear buffer")
		serial.ClearInputBuffer()
		e = errors.New("crc error")
	}
	return
}

// 封装数据
func packagingCommand(cmd byte, obj interface{}) ([]byte, error) {
	var b []byte
	var size int
	if obj != nil {
		b, size = getObjSize(obj)
	}

	var head = BaseInfo{
		Head:   0x3BB3,
		Des:    0x01,
		Source: 0x00,
		Type:   0x00,
		Id:     getNumber(),
		Cmd:    cmd,
		Length: uint16(size),
	}
	var end EndInfo
	// 转换协议头
	buf, e := conv.ObjectToBytes(head, conv.ASC)
	if e != nil {
		return nil, e
	}

	if b != nil {
		buf = append(buf, b...) // 添加数据
	}
	// 计算crc16
	dataSize := len(buf)
	c := crc.CheckCrc16(buf, dataSize)
	end.Crc16 = c
	//转换及添加协议尾
	b, _ = conv.ObjectToBytes(end, conv.ASC)
	buf = append(buf, b...)

	return buf, e
}

// 获取结构占用字节数量
func getObjSize(o interface{}) ([]byte, int) {
	b, e := conv.ObjectToBytes(o, conv.ASC)
	if e != nil {
		return nil, 0
	}
	return b, len(b)
}
